#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#define trimSpace(start) do{\
      while(isspace(*start))start++;\
          }while(0)

#define NUM 1024
#define OPT_NUM 64

#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2 
#define APPEND_REDIR 3
char lineCommand[NUM];
char* myargv[OPT_NUM];
int lastCode=0;
int lastSig=0;

int redirType = NONE_REDIR;
char *redirFile = NULL;

void commandCheck(char* commands)
{
  assert(commands);
  char* start= commands;
  char *end = commands +strlen(commands);
  while(start<end)
  {
    if(*start == '>')
    {
      *start = '\0';
      ++start;
      //ls -a >> file.log
      //ls -a > file.log
      if(*start == '>')
      {
        redirType = APPEND_REDIR;
      }
      else 
      {
        redirType = OUTPUT_REDIR;
        ++start;
      }
      //start指向了文件名
      trimSpace(start);
      redirFile = start;
      break;
    }
    else if(*start == '<')
    {
     // "cat < file.txt"
     *start = '\0';
     start++;
     trimSpace(start);
     //填写重定向信息
    redirType = INPUT_REDIR;
     redirFile = start;
     break;
    }
    else 
    {
      start++;
    }
  }
}

int main()
{
  while(1)
  {
    //输出提示符
    printf("用户名@主机名 当前路径# ");
    fflush(stdout);
    //获取用户输入
    char* s=fgets(lineCommand,sizeof(lineCommand)-1,stdin);
    assert(s!=NULL);
    //清除最后一个\n
    lineCommand[strlen(lineCommand)-1]=0;

    commandCheck(lineCommand);
    myargv[0] = strtok(lineCommand," ");
    int i = 1;
    if(myargv[0] !=NULL && strcmp(myargv[0],"ls")==0)
    {
      myargv[i++]=(char*) "--color=auto";
    }
    while(myargv[i++]=strtok(NULL," "));
    if(myargv[0]!=NULL && strcmp(myargv[0],"cd")==0)
    {
      if(myargv[1] != NULL)chdir(myargv[1]);
      continue;
    }
    if(myargv[0] != NULL && myargv[1] !=NULL && strcmp(myargv[1],"echo")==0)
    {
      if(strcmp(myargv[1],"$?")==0)
      {
        printf("%d, %d\n",lastCode,lastSig);
      }
      else 
      {
        printf("%s\n",myargv[1]);
        continue;
      }
    }
    pid_t id=fork();
    assert(id!=-1);
    if(id==0)
    {
      //因为命令是子进程执行的，重定向的工作一定要是子进程来完成
      //如何重定向 是父进程要给子进程提供信息的
      //这里重定向会影响父进程吗?

      switch(redirType)
      {
        case NONE_REDIR:
          //什么都不做
          break;
        case INPUT_REDIR:
          {
              int fd = open(redirFile,O_RDONLY);
              if(fd < 0)
              {
                perror("open");
                exit(errno);
              }
              //重定向的文件已经成功打开了
              dup2(fd,0);
          }
          break;
        case OUTPUT_REDIR:
        case APPEND_REDIR:
          {
            umask(0);
            int flags = O_WRONLY | O_CREAT;
            if(redirType == APPEND_REDIR) flags |= O_APPEND;
            else flags |= O_TRUNC;
            int fd = open(redirFile,flags,0666);
            if(fd<0)
            {
              perror("open");
              exit(2);
            }
            dup2(fd,1);
          }
          break;
        default:
          printf("bug?\n");
          break;
      }

      execvp(myargv[0],myargv);
      redirFile = NULL;
      redirType = NONE_REDIR;
      exit(1);
    }
    int status=0;
    pid_t ret=waitpid(id,&status,0);
    assert(ret>0);
    lastCode =((status>>8)&0xff);
    lastSig =(status& 0x7f);
  }

}
